
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

//------------------------------------------------------------
// shaco Framework
// Copyright © 2017-2021 chang.liu All rights reserved.
// Feedback: 449612236@qq.com
//------------------------------------------------------------

namespace shaco.Instance.Editor
{
    //批量设置mesh的pivot工具
    public class ChangeMeshPivotEditor : EditorWindow
    {
        class ChangePivotInfo
        {
            public GameObject target;
            public Mesh mesh;
            public MeshFilter meshFilter;
            public Collider collider;
            public Vector3 currentPivot;
            public Vector3 lastPivot;
        }

        ChangePivotInfo _currentInfo = new ChangePivotInfo();
        bool _isPivotChanged = false;

        [MenuItem("shaco/Tools/SetMeshPivotCenter")]
        static void SetAllSelectCenterPivot()
        {
            var willChangePivotyInfo = new List<ChangePivotInfo>();
            foreach (var iter in Selection.gameObjects)
            {
                if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(iter)))
                {
                    Debug.LogError("ChangeMeshPivotEditor SetAllSelectCenterPivot erorr: only can select in 'Hierarchy' window, target=" + iter, iter);
                    continue;
                }

                var newInfo = RecognizeSelectedObject(iter);

                if (null != newInfo)
                {
                    willChangePivotyInfo.Add(newInfo);
                }
            }

            if (willChangePivotyInfo.Count > 0)
            {
                foreach (var iter in willChangePivotyInfo)
                {
                    SetCenterPivot(iter);
                }
                AssetDatabase.SaveAssets();
            }
        }

        [MenuItem("shaco/Tools/ChangeMeshPivotWindow")]
        static void OpenWindow()
        {
            var window = (ChangeMeshPivotEditor)EditorWindow.GetWindow(typeof(ChangeMeshPivotEditor));
            window._currentInfo = RecognizeSelectedObject(Selection.activeGameObject);
            window.Show();
        }

        void OnDestroy()
        {
            CheckAutoSave();
        }

        void OnSelectionChange()
        {
            CheckAutoSave();
            _currentInfo = RecognizeSelectedObject(Selection.activeGameObject);
        }

        void OnGUI()
        {
            if (null == _currentInfo || null == _currentInfo.target)
            {
                GUILayout.Label("No object selected in Hierarchy.");
                return;
            }

            if (null == _currentInfo.mesh)
            {
                GUILayout.Label("Selected object does not have a Mesh specified.");
                return;
            }

            this.Repaint();
            _currentInfo.currentPivot.x = EditorGUILayout.Slider("X", _currentInfo.currentPivot.x, -1.0f, 1.0f);
            _currentInfo.currentPivot.y = EditorGUILayout.Slider("Y", _currentInfo.currentPivot.y, -1.0f, 1.0f);
            _currentInfo.currentPivot.z = EditorGUILayout.Slider("Z", _currentInfo.currentPivot.z, -1.0f, 1.0f);
            if (_currentInfo.currentPivot != _currentInfo.lastPivot)
            {
                _currentInfo.mesh = _currentInfo.meshFilter.sharedMesh;
                UpdatePivot(_currentInfo);
                _currentInfo.lastPivot = _currentInfo.currentPivot;
                _isPivotChanged = true;
            }

            if (GUILayout.Button("Center"))
            {
                SetCenterPivot(_currentInfo);
                AssetDatabase.SaveAssets();
            }

            if (_isPivotChanged && GUILayout.Button("Save"))
            {
                _isPivotChanged = false;
                AssetDatabase.SaveAssets();
            }
            GUILayout.Label("Bounds " + _currentInfo.mesh.bounds.ToString());
        }

        void CheckAutoSave()
        {
            if (_isPivotChanged)
            {
                _isPivotChanged = false;
                AssetDatabase.SaveAssets();
            }
        }

        static void SetCenterPivot(ChangePivotInfo info)
        {
            info.mesh = info.meshFilter.sharedMesh;
            info.currentPivot = Vector3.zero;
            UpdatePivot(info);
            info.lastPivot = info.currentPivot;
        }

        static Vector3 RotateVector3(Vector3 source, Vector3 axis, float angle)
        {
            Quaternion q = Quaternion.AngleAxis(angle, axis);
            return q * source;
        }

        static void UpdatePivot(ChangePivotInfo info)
        {
            Vector3 diff = Vector3.Scale(info.mesh.bounds.extents, info.lastPivot - info.currentPivot);
            var convertDiff = Vector3.Scale(diff, info.target.transform.localScale);
            convertDiff = RotateVector3(convertDiff, Vector3.forward, info.target.transform.localEulerAngles.z);
            convertDiff = RotateVector3(convertDiff, Vector3.right, info.target.transform.localEulerAngles.x);
            convertDiff = RotateVector3(convertDiff, Vector3.up, info.target.transform.localEulerAngles.y);
            info.target.transform.localPosition -= convertDiff;

            Vector3[] verts = info.mesh.vertices;
            for (int i = 0; i < verts.Length; ++i)
            {
                verts[i] += diff;
            }

            info.mesh.vertices = verts;
            info.mesh.RecalculateBounds();

            if (info.collider)
            {
                if (info.collider is BoxCollider)
                {
                    ((BoxCollider)info.collider).center += diff;
                }
                else if (info.collider is CapsuleCollider)
                {
                    ((CapsuleCollider)info.collider).center += diff;
                }
                else if (info.collider is SphereCollider)
                {
                    ((SphereCollider)info.collider).center += diff;
                }
            }

            var obstacle = info.target.GetComponent(typeof(UnityEngine.AI.NavMeshObstacle)) as UnityEngine.AI.NavMeshObstacle;
            if (null != obstacle)
                obstacle.center += diff;

            var prefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetPrefabStage(info.target);
            if (null != prefabStage)
            {
                UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(prefabStage.scene);
            }
            else
            {
                if (!Application.isPlaying)
                    UnityEditor.SceneManagement.EditorSceneManager.MarkAllScenesDirty();
            }
        }

        static bool UpdatePivotVector(ChangePivotInfo info)
        {
            Bounds b = info.mesh.bounds;

            //过滤没有大小的对象
            if ((0 == b.extents.x && 0 == b.extents.y && 0 == b.extents.z))
            {
                Debug.LogWarning("ChangeMeshPivotEditor UpdatePivotVector warning: no size, extents=" + b.extents + " target=" + info.target, info.target);
                return false;
            }

            Vector3 offset = -1 * b.center;
            info.currentPivot = info.lastPivot = new Vector3(offset.x / b.extents.x, offset.y / b.extents.y, offset.z / b.extents.z);
            return true;
        }

        static ChangePivotInfo RecognizeSelectedObject(GameObject target)
        {
            ChangePivotInfo retValue = null;
            if (null != target)
            {
                retValue = new ChangePivotInfo();
                retValue.target = target;
                retValue.meshFilter = target.GetComponent(typeof(MeshFilter)) as MeshFilter;
                retValue.mesh = retValue.meshFilter ? retValue.meshFilter.sharedMesh : null;
                retValue.collider = retValue.target.GetComponent(typeof(Collider)) as Collider;

                if (null != retValue.mesh)
                {
                    if (!UpdatePivotVector(retValue))
                    {
                        retValue = null;
                    }
                }
                else
                    retValue = null;
            }
            return retValue;
        }
    }
}